package com.example.demo.compile;



import com.example.demo.commom.FileOperations;
import org.springframework.context.annotation.Configuration;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User:龙宝
 * Date:2023-10-08
 * Time:15:39
 * 此类为用户提交的OJ代码的编译运行过程
 */
@Configuration
public class Task {
    //通过一组常量来约定零时文件的名字
    private String WOKE_DIR = null;//临时文件的所在目录
    private String CLASS = null;//约定代码的类名
    private String COMPILE_BE = null;//约定待编译的代码文件名
    private String COMPILE_ERROR = null;//存放编译错误信息的文件名
    private String STDOUT = null;//存放运行时的标准输出的文件名
    private String STDERR = null;//存放标准错误信息的文件名

    public Task() {
        //使用UUID这个类生成一个UUID,来区分不用的文件夹
        WOKE_DIR = "./tmp/" + UUID.randomUUID().toString() + "/";
        CLASS = "Solution";
        COMPILE_BE = WOKE_DIR + "Solution.java";
        COMPILE_ERROR = WOKE_DIR + "compileError.txt";
        STDOUT = WOKE_DIR + "stdout.txt";
        STDERR = WOKE_DIR + "stdeer.txt";
    }

    //执行用户代码进行编译和运行
    public Answer CompileAndRun(Question question) {
        Answer answer = new Answer();
        //1、准备用来存放临时文件的目录
        File workDir = new File(WOKE_DIR);
        if(!workDir.exists()) {
            //创建多级目录
            workDir.mkdirs();
        }
        //2、安全性判定
        if(!checkCodeSafe(question.getCode())) {
            //不安全则不能继续进行下去
            answer.setCode(3);
            answer.setMsgReason("您提交的代码存在违规代码，禁止运行！");
            return answer;
        }
        //3、把一个question中的code代码写入到一个Solution.Java文件中
        FileOperations.writeFile(COMPILE_BE,question.getCode());
        //4、创建子进程，调用javac进行编译
        //4.1先把命令构造出来
        String compileCmd = String.format("javac -encoding utf8 %s -d %s",COMPILE_BE,WOKE_DIR);
        //4.2、执行
        CommandExecute.run(compileCmd,null ,COMPILE_ERROR);//编译期间不关心他的标准输出，只关心他编译有没有出错
        //4.3、如果编译出错，错误信息就被记录到了COMPILE_ERROR中，如果没有出错，该文件为空
        String compileError = FileOperations.readFile(COMPILE_ERROR);
        if(!compileError.equals("")) {
            //不为空，编译出错，返回
            answer.setCode(1);
            answer.setMsgReason("编译出错!" + compileError);
            return answer;
        }
        //5、编译正确后，开始运行代码
        String runCmd = String.format("java -classpath %s %s",WOKE_DIR,CLASS);
        CommandExecute.run(runCmd,STDOUT,STDERR);
        String runError = FileOperations.readFile(STDERR);
        if(!runError.equals("")) {
            answer.setCode(2);
            answer.setMsgReason("运行出错!" + runError);
            return answer;
        }
        //6、代码能走到这里，说明用户提交的代码是可以运行
        //父进程获取到刚才的运行后的结果，并且打包成compile.Answer对象
        //检查是否可以运行通过
        String runStdout = FileOperations.readFile(STDOUT);
        //用例没有完全通过
//        if(!runStdout.equals("运行通过！")) {
        if(!runStdout.equals("-1\r\n")) {
            answer.setCode(4);
            answer.setMsgReason(runStdout);
            return answer;
        }
        answer.setCode(0);
        answer.setStdout(FileOperations.readFile(STDOUT));
        return answer;
    }

    private boolean checkCodeSafe(String code) {
        List<String> blackList = new ArrayList<>();
        //防止提交的代码恶意运行程序
        blackList.add("Runtime");
        blackList.add("exec");
        //禁止提交代码操作文件
        blackList.add("java.io");
        //禁止提交代码访问网络
        blackList.add("java.net");

        for(String target : blackList) {
            int pos = code.indexOf(target);
            if(pos >= 0) {
                return false;//找到了恶意代码，返回false1表示不安全
            }
        }
        return true;
    }
}
